﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Dispatcher;
using System.Web.Http.Routing;

namespace WebApiVersionControl.Version
{
    public class VersionHttpControllerSelector : IHttpControllerSelector
    {
        #region 
        private const string VersionKey = "Values";
        private const string ControllerKey = "controller";


        private readonly HttpConfiguration _configuration;

        private readonly Lazy<Dictionary<string, HttpControllerDescriptor>> _controllers;

        private readonly HashSet<string> _duplicates;

        public VersionHttpControllerSelector(HttpConfiguration config)
        {
            _configuration = config;
            _duplicates = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
            _controllers = new Lazy<Dictionary<string, HttpControllerDescriptor>>(InitializeControllerDictionary);
        }

        private Dictionary<string, HttpControllerDescriptor> InitializeControllerDictionary()
        {
            var dictionary = new Dictionary<string, HttpControllerDescriptor>(StringComparer.OrdinalIgnoreCase);
            IAssembliesResolver assembliesResolver = _configuration.Services.GetAssembliesResolver();
            IHttpControllerTypeResolver controllersResolver = _configuration.Services.GetHttpControllerTypeResolver();

            ICollection<Type> controllerTypes = controllersResolver.GetControllerTypes(assembliesResolver);
            foreach (Type t in controllerTypes)
            {
                var segments = t.Namespace.Split(Type.Delimiter);
                //对于dictionary键，从类型名称的末尾去掉“Controller”。
                var controllerName = t.Name.Remove(t.Name.Length - DefaultHttpControllerSelector.ControllerSuffix.Length);
                string version = segments[segments.Length - 1];
                var key = String.Format(CultureInfo.InvariantCulture, $"{version}.{controllerName}");
                if (version == "Controllers")
                {
                    key = String.Format(CultureInfo.InvariantCulture, $"{controllerName}");
                }
                //检测 key 是否重名
                if (dictionary.Keys.Contains(key))
                {
                    _duplicates.Add(key);
                }
                else
                {
                    dictionary[key] = new HttpControllerDescriptor(_configuration, t.Name, t);
                }
            }

            //从字典中删除任何重复项，因为它们会创建不明确的匹配项。
            //例如，“Foo.V1.ProductsController”和“Bar.V1.ProductsController”都映射到“V1.products”。
            foreach (string s in _duplicates)
            {
                dictionary.Remove(s);
            }
            return dictionary;
        }

        //从路由中获取值
        private static T GetRouteVariable<T>(IHttpRouteData routeData, string name)
        {
            object result = null;
            if (routeData.Values.TryGetValue(name, out result))
            {
                return (T)result;
            }
            return default(T);
        }

        public HttpControllerDescriptor SelectController(HttpRequestMessage request)
        {
            //通过路由获取数据
            IHttpRouteData routeData = request.GetRouteData();
            if (routeData == null)
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }
            //从路由数据获取版本和控制器变量
            string version = GetRouteVariable<string>(routeData, VersionKey);
            if (string.IsNullOrEmpty(version))
            {
                version = GetVersionFromHTTPHeaderAndAcceptHeader(request);
            }
            //获取控制器下请求的方法
            string controllerName = GetRouteVariable<string>(routeData, ControllerKey);
            if (controllerName == null)
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }
            //查找匹配的控制器
            string key = String.Format(CultureInfo.InvariantCulture, $"{controllerName}");
            if (!string.IsNullOrEmpty(version))
            {
                key = String.Format(CultureInfo.InvariantCulture, $"{version}.{controllerName}");
            }

            HttpControllerDescriptor controllerDescriptor;
            if (_controllers.Value.TryGetValue(key, out controllerDescriptor))
            {
                return controllerDescriptor;
            }
            else if (_duplicates.Contains(key))
            {
                throw new HttpResponseException(request.CreateErrorResponse(
                    HttpStatusCode.InternalServerError,
                    "找到多个与此请求匹配的控制器"));
            }
            else
            {
                throw new HttpResponseException(HttpStatusCode.NotFound);
            }
        }

        public IDictionary<string, HttpControllerDescriptor> GetControllerMapping()
        {
            return _controllers.Value;
        }

        private string GetVersionFromHTTPHeaderAndAcceptHeader(HttpRequestMessage requestMessage)
        {
            if (requestMessage.Headers.Contains(VersionKey))
            {
                var versionHeader = requestMessage.Headers.GetValues(VersionKey).FirstOrDefault();
                if (versionHeader != null)
                {
                    return versionHeader;
                }
            }
            var acceptHeader = requestMessage.Headers.Accept;
            foreach (var item in acceptHeader)
            {
                if (item.MediaType == "application/json" || item.MediaType == "text/html")
                {
                    var version = item.Parameters.Where(v => v.Name.Equals(VersionKey, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
                    if (version != null)
                    {
                        return version.Value;
                    }
                    return string.Empty;
                }
            }
            return string.Empty;
        }
        #endregion
    }
}